/*
* Fabric3
* Copyright (c) 2009-2015 Metaform Systems
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.fabric3.assembly;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Enumeration;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import org.apache.maven.model.Dependency;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.eclipse.aether.RepositorySystem;
import org.eclipse.aether.RepositorySystemSession;
import org.eclipse.aether.artifact.Artifact;
import org.eclipse.aether.artifact.DefaultArtifact;
import org.eclipse.aether.repository.RemoteRepository;
import org.eclipse.aether.resolution.ArtifactRequest;
import org.eclipse.aether.resolution.ArtifactResolutionException;
import org.eclipse.aether.resolution.ArtifactResult;
/**
* Plugin that builds a Fabric3 runtime image based on a set of profiles and/or extensions. Standalone and Tomcat runtime images are supported.
*
* @goal fabric3-assembly
* @phase generate-resources
* @threadSafe
*/
@SuppressWarnings("ResultOfMethodCallIgnored")
public class Fabric3RuntimeAssemblyMojo extends AbstractMojo {
private static final int BUFFER = 2048;
private static final String RUNTIME_STANDALONE = "standalone";
private static final String RUNTIME_TOMCAT = "tomcat";
/**
* Runtime configuration where the contributions should be copied.
*
* @parameter
*/
public String contributionTarget = "vm";
/**
* True if non-target runtime configurations should be removed.
*
* @parameter
*/
public boolean clean;
/**
* Directory where the runtime image is built.
*
* @parameter property="project.build.directory"
* @required
*/
public File buildDirectory;
/**
* Project source directory.
*
* @parameter property="project.build.sourceDirectory"
* @required
*/
public File sourceDirectory;
/**
* The default version of the runtime to use.
*
* @parameter property="RELEASE"
*/
public String runtimeVersion;
/**
* Sets the runtime type, standalone or Tomcat
*
* @parameter
*/
public String type = "standalone";
/**
* Set of profiles for the runtime.
*
* @parameter
*/
public Dependency[] profiles = new Dependency[0];
/**
* Set of extensions for the runtime.
*
* @parameter
*/
public Dependency[] extensions = new Dependency[0];
/**
* Set of configuration files
*
* @parameter
*/
public ConfigFile[] configurationFiles = new ConfigFile[0];
/**
* Set of extensions to remove for the runtime.
*
* @parameter
*/
public Dependency[] removeExtensions = new Dependency[0];
/**
* Set of contributions to install in the runtime.
*
* @parameter
*/
public Dependency[] contributions = new Dependency[0];
/**
* Set of datasources to install in the runtime.
*
* @parameter
*/
public Dependency[] datasources = new Dependency[0];
/**
* Set of jndi dependencies to install in the runtime.
*
* @parameter
*/
public Dependency[] jndiDependencies = new Dependency[0];
/**
* @component
*/
public RepositorySystem repositorySystem;
/**
* The current repository/network configuration of Maven.
*
* @parameter default-value="${repositorySystemSession}"
* @readonly
*/
public RepositorySystemSession session;
/**
* The project's remote repositories to use for the resolution of project dependencies.
*
* @parameter default-value="${project.remoteProjectRepositories}"
* @readonly
*/
private List<RemoteRepository> projectRepositories;
public Fabric3RuntimeAssemblyMojo() {
}
public void execute() throws MojoExecutionException {
String artifactId;
File baseDirectory = new File(buildDirectory, "image");
baseDirectory.mkdirs();
File rootDirectory;
if (RUNTIME_STANDALONE.equalsIgnoreCase(type)) {
artifactId = "runtime-standalone";
rootDirectory = baseDirectory;
} else if (RUNTIME_TOMCAT.equalsIgnoreCase(type)) {
artifactId = "runtime-tomcat";
// tomcat is installed as <tomcat home>/fabric3
rootDirectory = new File(baseDirectory, "fabric3");
} else {
throw new MojoExecutionException("Invalid runtime type specified: " + type);
}
extractRuntime("org.fabric3", artifactId, baseDirectory);
installProfiles(rootDirectory);
installExtensions(rootDirectory);
installDatasources(rootDirectory);
installJndiDependencies(rootDirectory);
installContributions(rootDirectory);
installConfiguration(rootDirectory);
removeExtensions(rootDirectory);
if (clean) {
cleanRuntimes(rootDirectory);
}
}
/**
* Cleans the runtime directories.
*
* @param rootDirectory root runtime image
* @throws MojoExecutionException if there is an error
*/
private void cleanRuntimes(File rootDirectory) throws MojoExecutionException {
File runtimes = new File(rootDirectory, "runtimes");
for (File file : runtimes.listFiles()) {
if (file.isDirectory() && !contributionTarget.equals(file.getName())) {
try {
FileHelper.forceDelete(file);
} catch (IOException e) {
getLog().error(e);
throw new MojoExecutionException(e.getMessage());
}
}
}
}
/**
* Resolves and unzips the contents of a base runtime distribution to a directory.
*
* @param groupId the distribution group id
* @param artifactId the distribution artifact id
* @param baseDirectory the extract directory
* @throws MojoExecutionException if there is an error extracting the distribution
*/
private void extractRuntime(String groupId, String artifactId, File baseDirectory) throws MojoExecutionException {
Artifact artifact = new DefaultArtifact(groupId, artifactId, "bin", "zip", runtimeVersion);
try {
getLog().info("Installing the Fabric3 runtime");
ArtifactResult result = repositorySystem.resolveArtifact(session, new ArtifactRequest(artifact, projectRepositories, null));
File source = result.getArtifact().getFile();
extract(source, baseDirectory);
} catch (ArtifactResolutionException e) {
throw new MojoExecutionException(e.getMessage(), e);
}
}
/**
* Resolves and installs a set of configured profiles by extracting their contents to a runtime image repository
*
* @param rootDirectory the top-level runtime image directory
* @throws MojoExecutionException if there is an error during installation
*/
private void installProfiles(File rootDirectory) throws MojoExecutionException {
for (Dependency profile : profiles) {
String groupId = profile.getGroupId();
String artifactId = profile.getArtifactId();
String version = profile.getVersion();
getLog().info("Installing profile: " + groupId + ":" + artifactId);
Artifact artifact = new DefaultArtifact(groupId, artifactId, "bin", "zip", version);
try {
ArtifactResult result = repositorySystem.resolveArtifact(session, new ArtifactRequest(artifact, projectRepositories, null));
File source = result.getArtifact().getFile();
extract(source, rootDirectory);
} catch (ArtifactResolutionException e) {
throw new MojoExecutionException(e.getMessage(), e);
}
}
}
/**
* Resolves and installs a set of configured extensions by copying them to a runtime image repository
*
* @param rootDirectory the top-level runtime image directory
* @throws MojoExecutionException if there is an error during installation
*/
private void installExtensions(File rootDirectory) throws MojoExecutionException {
for (Dependency extension : extensions) {
String groupId = extension.getGroupId();
String artifactId = extension.getArtifactId();
String version = extension.getVersion();
String type = extension.getType();
String classifier = extension.getClassifier();
getLog().info("Installing extension: " + groupId + ":" + artifactId);
Artifact artifact = new DefaultArtifact(groupId, artifactId, classifier, type, version);
ArtifactResult result;
try {
result = repositorySystem.resolveArtifact(session, new ArtifactRequest(artifact, projectRepositories, null));
} catch (ArtifactResolutionException e) {
throw new MojoExecutionException(e.getMessage(), e);
}
File source = result.getArtifact().getFile();
InputStream sourceStream = null;
OutputStream targetStream = null;
try {
File repository = new File(rootDirectory, "extensions");
sourceStream = new BufferedInputStream(new FileInputStream(source));
File targetFile = new File(repository, source.getName());
targetStream = new BufferedOutputStream(new FileOutputStream(targetFile));
copy(sourceStream, targetStream);
} catch (IOException e) {
throw new MojoExecutionException(e.getMessage(), e);
} finally {
close(targetStream);
close(sourceStream);
}
}
}
/**
* Installs configuration files to the server image.
*
* @param rootDirectory the top-level runtime image directory
* @throws MojoExecutionException if there is an error during installation
*/
private void installConfiguration(File rootDirectory) throws MojoExecutionException {
for (ConfigFile file : configurationFiles) {
InputStream sourceStream = null;
OutputStream targetStream = null;
try {
// main directory is parent of the source directory
File source = new File(sourceDirectory.getParent(), file.getSource());
File targetDirectory = new File(rootDirectory, file.getDestination());
targetDirectory.mkdirs();
sourceStream = new BufferedInputStream(new FileInputStream(source));
File targetFile = new File(targetDirectory, source.getName());
targetStream = new BufferedOutputStream(new FileOutputStream(targetFile));
copy(sourceStream, targetStream);
} catch (IOException e) {
throw new MojoExecutionException(e.getMessage(), e);
} finally {
close(targetStream);
close(sourceStream);
}
}
}
/**
* Installs contributions to the deploy directory.
*
* @param rootDirectory the top-level runtime image directory
* @throws MojoExecutionException if there is an error during installation
*/
private void installContributions(File rootDirectory) throws MojoExecutionException {
for (Dependency contribution : contributions) {
String groupId = contribution.getGroupId();
String artifactId = contribution.getArtifactId();
String version = contribution.getVersion();
String type = contribution.getType();
String classifier = contribution.getClassifier();
getLog().info("Installing contribution: " + groupId + ":" + artifactId);
Artifact artifact = new DefaultArtifact(groupId, artifactId, classifier, type, version);
ArtifactResult result;
try {
result = repositorySystem.resolveArtifact(session, new ArtifactRequest(artifact, projectRepositories, null));
} catch (ArtifactResolutionException e) {
throw new MojoExecutionException(e.getMessage(), e);
}
File source = result.getArtifact().getFile();
InputStream sourceStream = null;
OutputStream targetStream = null;
try {
File repository = new File(rootDirectory, "runtimes" + File.separator + contributionTarget + File.separatorChar + "deploy");
repository.mkdirs();
sourceStream = new BufferedInputStream(new FileInputStream(source));
File targetFile = new File(repository, source.getName());
targetStream = new BufferedOutputStream(new FileOutputStream(targetFile));
copy(sourceStream, targetStream);
} catch (IOException e) {
throw new MojoExecutionException(e.getMessage(), e);
} finally {
close(targetStream);
close(sourceStream);
}
}
}
/**
* Resolves and installs a set of configured datasource dependencies.
*
* @param rootDirectory the top-level runtime image directory
* @throws MojoExecutionException if there is an error during installation
*/
private void installDatasources(File rootDirectory) throws MojoExecutionException {
if (datasources == null || datasources.length == 0) {
return;
}
File repository = new File(rootDirectory, "extensions");
File datasourceDir = new File(repository, "datasource");
datasourceDir.mkdirs();
for (Dependency dependency : datasources) {
String groupId = dependency.getGroupId();
String artifactId = dependency.getArtifactId();
String version = dependency.getVersion();
String type = dependency.getType();
String classifier = dependency.getClassifier();
getLog().info("Installing datasource library: " + groupId + ":" + artifactId);
Artifact artifact = new DefaultArtifact(groupId, artifactId, classifier, type, version);
// Artifact artifact = artifactFactory.createArtifactWithClassifier(groupId, artifactId, version, type, classifier);
ArtifactResult result;
try {
result = repositorySystem.resolveArtifact(session, new ArtifactRequest(artifact, projectRepositories, null));
} catch (ArtifactResolutionException e) {
throw new MojoExecutionException(e.getMessage(), e);
}
File source = result.getArtifact().getFile();
InputStream sourceStream = null;
OutputStream targetStream = null;
try {
sourceStream = new BufferedInputStream(new FileInputStream(source));
File targetFile = new File(datasourceDir, source.getName());
targetStream = new BufferedOutputStream(new FileOutputStream(targetFile));
copy(sourceStream, targetStream);
} catch (IOException e) {
throw new MojoExecutionException(e.getMessage(), e);
} finally {
close(targetStream);
close(sourceStream);
}
}
}
/**
* Resolves and installs a set of configured jndi dependencies.
*
* @param rootDirectory the top-level runtime image directory
* @throws MojoExecutionException if there is an error during installation
*/
private void installJndiDependencies(File rootDirectory) throws MojoExecutionException {
if (jndiDependencies == null || jndiDependencies.length == 0) {
return;
}
File repository = new File(rootDirectory, "extensions");
File jndiDir = new File(repository, "jndi");
jndiDir.mkdirs();
for (Dependency dependency : jndiDependencies) {
String groupId = dependency.getGroupId();
String artifactId = dependency.getArtifactId();
String version = dependency.getVersion();
String type = dependency.getType();
String classifier = dependency.getClassifier();
getLog().info("Installing jndi library: " + groupId + ":" + artifactId);
Artifact artifact = new DefaultArtifact(groupId, artifactId, classifier, type, version);
ArtifactResult result;
try {
result = repositorySystem.resolveArtifact(session, new ArtifactRequest(artifact, projectRepositories, null));
} catch (ArtifactResolutionException e) {
throw new MojoExecutionException(e.getMessage(), e);
}
File source = result.getArtifact().getFile();
InputStream sourceStream = null;
OutputStream targetStream = null;
try {
sourceStream = new BufferedInputStream(new FileInputStream(source));
File targetFile = new File(jndiDir, source.getName());
targetStream = new BufferedOutputStream(new FileOutputStream(targetFile));
copy(sourceStream, targetStream);
} catch (IOException e) {
throw new MojoExecutionException(e.getMessage(), e);
} finally {
close(targetStream);
close(sourceStream);
}
}
}
/**
* Removes extensions from the server image.
*
* @param rootDirectory the top-level runtime image directory
* @throws MojoExecutionException if there is an error
*/
private void removeExtensions(File rootDirectory) throws MojoExecutionException {
for (Dependency extension : removeExtensions) {
String id = extension.getArtifactId();
String version = extension.getVersion();
if (version == null) {
throw new MojoExecutionException("Version not specified for: " + id);
}
String fileName = id + "-" + version + ".jar";
File extensionsDir = new File(rootDirectory, "extensions");
File file = new File(extensionsDir, fileName);
boolean result = file.delete();
if (!result) {
throw new MojoExecutionException("Unable to exclude: " + file);
}
}
}
/**
* Extracts the contents of a zip file to a target directory.
*
* @param source the zip file
* @param destination the target directory
* @throws MojoExecutionException if there is an error during extraction
*/
private void extract(File source, File destination) throws MojoExecutionException {
ZipFile zipfile;
try {
zipfile = new ZipFile(source);
} catch (IOException e) {
throw new MojoExecutionException(e.getMessage(), e);
}
Enumeration enumeration = zipfile.entries();
while (enumeration.hasMoreElements()) {
ZipEntry entry = (ZipEntry) enumeration.nextElement();
if (entry.isDirectory()) {
new File(destination, entry.getName()).mkdirs();
} else {
if (entry.getName().toUpperCase().endsWith(".MF")) {
// ignore manifests
continue;
}
InputStream sourceStream = null;
OutputStream targetStream = null;
try {
sourceStream = new BufferedInputStream(zipfile.getInputStream(entry));
FileOutputStream fos = new FileOutputStream(new File(destination, entry.getName()));
targetStream = new BufferedOutputStream(fos, BUFFER);
copy(sourceStream, targetStream);
targetStream.flush();
} catch (IOException e) {
throw new MojoExecutionException(e.getMessage(), e);
} finally {
close(targetStream);
close(sourceStream);
}
}
}
}
private int copy(InputStream input, OutputStream output) throws IOException {
byte[] buffer = new byte[BUFFER];
int count = 0;
int n;
while (-1 != (n = input.read(buffer))) {
output.write(buffer, 0, n);
count += n;
}
return count;
}
private void close(Closeable closeable) {
try {
if (closeable != null) {
closeable.close();
}
} catch (IOException e) {
getLog().error(e);
}
}
}